
/*
 uart, channel: 
 消息发送之前使用 hdlc 编码, 这样在字符流中能够定义边界和长度.
 编码之前的最大的消息长度是 64 个字节.
 编码之后 应该不会超过 128 个字节.
*/


#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>


#include "rp2040.h"
#include "hardware/clocks.h"
#include "hardware/gpio.h"
#include "hardware/uart.h"

#include "msgpool.h"
#include "umsg.h"


/*
0x7E : 标识 frame 的开始和结束.

需要转义的序列如下:
0x7E : 0x7D 0x5E
0x7D : 0x7D 0x5D

return : 编码之后的字节长度.
*/
static int hdlc_encode( umsg_t * pmsg, uint8_t * pout )
{
	int i;
	int ofs;
	uint8_t temp;

	/* frame begin. */
	ofs = 0;
	pout[ofs] = 0x7E;
	ofs += 1;

	/**/
	for ( i=0; i<pmsg->mlen; i++ ) {

		temp = pmsg->data[i];

		if ( temp == 0x7E ) {
			
			pout[ofs] = 0x7D;
			ofs += 1;

			pout[ofs] = 0x5E;
			ofs += 1;

		} else if ( temp == 0x7D ) {

			pout[ofs] = 0x7D;
			ofs += 1;

			pout[ofs] = 0x5D;
			ofs += 1;

		} else {

			pout[ofs] = temp;
			ofs += 1;
		}

	}

 	/* frame end.*/
	pout[ofs] = 0x7E;
	ofs += 1;
	return ofs;
}


/*
  在接收新的消息之前, 重置状态.
*/
static int hdlc_reset( umsg_t * pmsg )
{
	pmsg->type = 0;
	pmsg->mlen = 0;
	return 0;
}



/*
  type = 0 : 没有找到开始的 0x7E..
  type = 1 : 已经找到开始 0x7E..
  type = 0x7D : 开始之后, 刚刚吃进一个 escape 0x7D 字符.

  return : 0, 解码成功, 生成一个完整消息.
*/
static int hdlc_decode( uint8_t ich, umsg_t * pmsg )
{
	if ( ich == 0x7E ) {

		if ( pmsg->type == 0 ) {
			
			// frame begin...
			pmsg->type = 1;
			return 2;

		} else if ( pmsg->type == 1 ) {
			
			if ( pmsg->mlen == 0 ) {
				// 某种错误恢复.
				return 1;
			} else {

				// frame end..
				return 0;	
			}

		} else {

			// error..
			return -1;
		}
		
	}

	if ( pmsg->mlen >= 64 ) {
		// too long..
		return -2;
	}


	if ( pmsg->type == 0 ) {

		// 没有找到开始 0x7E, 仅仅丢弃这个输入.
		return 1;

	} else if ( pmsg->type == 1 ) {

		if ( ich == 0x7D ) {
			
			pmsg->type = 0x7D;

		} else {

			pmsg->data[pmsg->mlen] = ich;
			pmsg->mlen += 1;
		
		}

		return 3;

	} else if ( pmsg->type == 0x7D ) {

		if ( ich == 0x5E ) {

			pmsg->data[pmsg->mlen] = 0x7E;
			pmsg->mlen += 1;
			return 4;

		} else if ( ich == 0x5D ) {
			
			pmsg->data[pmsg->mlen] = 0x7D;
			pmsg->mlen += 1;
			return 4;

		} else {
			return -2;
		}

	} else {

		// error
		return -3;

	}

}

extern void umsg_send_to_host( void * pifc, int idx, umsg_t * pmsg );

static umsg_t * rxmsg = NULL;
static uint32_t rxcnt = 0;

void  Uart_1_Handler( void )
{
	uart_hw_t * puart = uart1_hw;
	uint32_t temp;
	int iret;

	/**/

	if ( rxmsg == NULL ) {
		rxmsg = (umsg_t *)msg_alloc();
		hdlc_reset(rxmsg);
	}

	/* read */
	while ( (puart->fr & UART_UARTFR_RXFE_BITS) == 0 ) {
		
		static umsg_t * tmsg;

		rxcnt += 1;
		temp = puart->dr;

		tmsg = (umsg_t *)msg_alloc();
		tmsg->type = 0;
		tmsg->mlen = 4;
		tmsg->data[0] = temp & 0xff;
		tmsg->data[1] = (temp >> 8) & 0xff;
		tmsg->data[2] = 0;
		tmsg->data[3] = 0;
		umsg_send_to_host( NULL, 0, tmsg );

		/**/
		iret = hdlc_decode( (uint8_t)(temp & 0xFF), rxmsg );

		if ( iret < 0 ) {
			hdlc_reset(rxmsg);
			continue;
		}

		if ( iret == 0 ) {
			// printf("rx succ packet.\n");
			rxmsg->type = 0;
			umsg_send_to_host( NULL, 0, rxmsg );

			/**/
			rxmsg = (umsg_t *)msg_alloc();
			hdlc_reset(rxmsg);
		}

	}

	return;
}


int utchan_init()
{
	/* clock */
	hw_set_bits( &clocks_hw->wake_en1, CLOCKS_WAKE_EN1_CLK_SYS_UART1_BITS | CLOCKS_WAKE_EN1_CLK_PERI_UART1_BITS );

	/* gpio-0:uart0-tx,  gpio-1:uart0-rx */
	// gpio_pull_up( 8 );
	// gpio_pull_up( 9 );
	gpio_set_function( 8, GPIO_FUNC_UART );
	gpio_set_function( 9, GPIO_FUNC_UART );

	/**/
	uart_init( uart1, 115200 );

	/* interrupt enable */
	uart_set_irq_enables( uart1, true, false );
	NVIC_EnableIRQ( UART1_IRQ_IRQn );
	return 0;
}

